package com.ityuwen.mengyagushi.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.ityuwen.mengyagushi.account.client.RechargeInfoFeignClient;
import com.ityuwen.mengyagushi.common.constant.KafkaConstant;
import com.ityuwen.mengyagushi.common.constant.SystemConstant;
import com.ityuwen.mengyagushi.common.result.Result;
import com.ityuwen.mengyagushi.common.service.KafkaService;
import com.ityuwen.mengyagushi.common.util.AuthContextHolder;
import com.ityuwen.mengyagushi.model.account.RechargeInfo;
import com.ityuwen.mengyagushi.model.order.OrderInfo;
import com.ityuwen.mengyagushi.model.payment.PaymentInfo;
import com.ityuwen.mengyagushi.model.user.UserInfo;
import com.ityuwen.mengyagushi.order.client.OrderInfoFeignClient;
import com.ityuwen.mengyagushi.payment.config.WxPayV3Config;
import com.ityuwen.mengyagushi.payment.service.PaymentInfoService;
import com.ityuwen.mengyagushi.payment.service.WxPayService;
import com.ityuwen.mengyagushi.payment.util.PayUtil;
import com.ityuwen.mengyagushi.user.client.UserInfoFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    @Autowired
    private RechargeInfoFeignClient rechargeInfoFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private JsapiServiceExtension service;

    @Autowired
    private WxPayV3Config wxPayV3Config;

	@Autowired
	private NotificationParser parser;

	@Autowired
	private KafkaService kafkaService;

    @Override
    public Map<String, String> createJsapi(String orderType, String orderNo) {
        Long userId = AuthContextHolder.getUserId();
        OrderInfo orderInfo = null;
        RechargeInfo rechargeInfo = null;
        if (SystemConstant.PAYMENT_TYPE_ORDER.equals(orderType)){
            // 查询订单
            Result<OrderInfo> orderInfoResult = this.orderInfoFeignClient.getOrderInfo(orderNo);
            Assert.notNull(orderInfoResult, "生成预支付单时，获取订单信息失败！");
            orderInfo = orderInfoResult.getData();
            Assert.notNull(orderInfo, "生成预支付单时，对应的订单不存在！");
            // 订单支付状态的校验：必须是未支付
            if (!SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
                throw new RuntimeException("该订单不能支付！");
            }

            // 校验订单是否属于当前用户
            if (userId != orderInfo.getUserId()) {
                throw new RuntimeException("该订单不属于您！");
            }
        } else if (SystemConstant.PAYMENT_TYPE_RECHARGE.equals(orderType)){
            Result<RechargeInfo> rechargeInfoResult = this.rechargeInfoFeignClient.getRechargeInfo(orderNo);
            Assert.notNull(rechargeInfoResult, "生成预支付单时，获取充值记录失败！");
            rechargeInfo = rechargeInfoResult.getData();
            Assert.notNull(rechargeInfo, "生成预支付单时，对应的充值记录不存在！");
            // 充值支付状态的校验：必须是未支付
            if (!SystemConstant.ORDER_STATUS_UNPAID.equals(rechargeInfo.getRechargeStatus())) {
                throw new RuntimeException("该充值不能支付！");
            }

            // 校验充值是否属于当前用户
            if (userId != rechargeInfo.getUserId()) {
                throw new RuntimeException("该充值不属于您！");
            }
        }

        // 调用微信支付接口，生成预支付单，并获取支付所需的参数
        PrepayRequest request = new PrepayRequest();
        request.setAppid(this.wxPayV3Config.getAppid());
        request.setMchid(this.wxPayV3Config.getMerchantId());
        request.setDescription("谷粒随享支付平台");
        request.setOutTradeNo(orderNo);
        request.setNotifyUrl(this.wxPayV3Config.getNotifyUrl());
        // 支付金额
        Amount amount = new Amount();
        // 正常应该设置为订单实际金额，但是这里请注意写1
        amount.setTotal(1);
        request.setAmount(amount);
        // 支付者
        Payer payer = new Payer();
        // 根据用户id查询用户
        Result<UserInfo> userInfoResult = this.userInfoFeignClient.getUserInfo(userId);
        Assert.notNull(userInfoResult, "生成预支付单时，获取用户信息失败！");
        UserInfo userInfo = userInfoResult.getData();
        Assert.notNull(userInfo, "生成预支付单时，获取用户信息失败！");
        payer.setOpenid(userInfo.getWxOpenId());
        request.setPayer(payer);
        PrepayWithRequestPaymentResponse response = this.service.prepayWithRequestPayment(request);

        // 生成对账记录
        if (SystemConstant.PAYMENT_TYPE_ORDER.equals(orderType)){
            this.savePayment(orderInfo);
        } else if(SystemConstant.PAYMENT_TYPE_RECHARGE.equals(orderType)) {
            this.savePayment(rechargeInfo);
        }

        // 把支付参数响应给小程序
        Map<String, String> map = new HashMap<>();
        map.put("timeStamp", response.getTimeStamp());
        map.put("nonceStr", response.getNonceStr());
        map.put("package", response.getPackageVal());
        map.put("signType", response.getSignType());
        map.put("paySign", response.getPaySign());
        return map;
    }

    @Override
    public Map<String, String> payOk(HttpServletRequest request) {
		Map<String, String> map = new HashMap<>();
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .body(PayUtil.readData(request))
                .build();

		try {
			// 以支付通知回调为例，验签、解密并转换成 Transaction
			Transaction transaction = parser.parse(requestParam, Transaction.class);
			if (!Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
				map.put("code", "FAIL");
				return map;
			}
			// 更新对账记录
			this.updatePaymentStatus(transaction);

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return null;
    }

    @Override
    public Boolean queryPayStatus(String orderNo) {

        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(this.wxPayV3Config.getMerchantId());
        queryRequest.setOutTradeNo(orderNo);

        try {
            Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
            if(Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())){
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    private void updatePaymentStatus(Transaction transaction){
		PaymentInfo paymentInfo = this.paymentInfoService.getOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, transaction.getOutTradeNo()));
		if (paymentInfo == null){
			return ;
		}
		paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_PAID);
		paymentInfo.setOutTradeNo(transaction.getTransactionId());
		paymentInfo.setCallbackTime(new Date());
		paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
        if (this.paymentInfoService.updateById(paymentInfo)) {
            if (paymentInfo.getPaymentType().equals(SystemConstant.PAYMENT_TYPE_ORDER)){
                // 发送消息更新订单状态
                this.kafkaService.sendMsg(KafkaConstant.QUEUE_ORDER_PAY_SUCCESS, transaction.getOutTradeNo());
            } else {
                // 发送消息更新充值记录的状态
                this.kafkaService.sendMsg(KafkaConstant.QUEUE_RECHARGE_PAY_SUCCESS, transaction.getOutTradeNo());
            }
        }
	}

    private void savePayment(OrderInfo orderInfo) {
        // 查询该订单是否已存在对账记录，如果存在则使用已有的
        PaymentInfo paymentInfo = this.paymentInfoService.getOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, orderInfo.getOrderNo()));
        if (paymentInfo != null) {
            return;
        }
        paymentInfo = new PaymentInfo();
        paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_UNPAID);
        paymentInfo.setAmount(orderInfo.getOrderAmount());
        paymentInfo.setContent(orderInfo.getOrderTitle());
        paymentInfo.setPaymentType(SystemConstant.PAYMENT_TYPE_ORDER);
        paymentInfo.setPayWay(orderInfo.getPayWay());
        paymentInfo.setOrderNo(orderInfo.getOrderNo());
        paymentInfo.setUserId(orderInfo.getUserId());
        this.paymentInfoService.save(paymentInfo);
    }

    private void savePayment(RechargeInfo rechargeInfo) {
        // 查询该订单是否已存在对账记录，如果存在则使用已有的
        PaymentInfo paymentInfo = this.paymentInfoService.getOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, rechargeInfo.getOrderNo()));
        if (paymentInfo != null) {
            return;
        }
        paymentInfo = new PaymentInfo();
        paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_UNPAID);
        paymentInfo.setAmount(rechargeInfo.getRechargeAmount());
        paymentInfo.setContent("余额充值");
        paymentInfo.setPaymentType(SystemConstant.PAYMENT_TYPE_RECHARGE);
        paymentInfo.setPayWay(rechargeInfo.getPayWay());
        paymentInfo.setOrderNo(rechargeInfo.getOrderNo());
        paymentInfo.setUserId(rechargeInfo.getUserId());
        this.paymentInfoService.save(paymentInfo);
    }
}
